﻿#pragma once

#include <chrono>
#include "Misc/CommonMacros.h"
#include "Object/NewObject.h"

#include "Event/MouseEvent.h"
#include "Event/KeyboardEvent.h"
#include "Event/WindowEvent.h"

#include "Containers/Optional.h"

#include "ApplicationArgument.h"
#include "ApplicationSubsystem.h"
#include "SubsystemManager.h"
#include "Input/Keys.h"
#include "Containers/ScriptDelegateImplement.h"
#include "TickableManager.h"
#include "Application.m.h"

class CGarbageCollector;
class RSubsystemManager;

RCLASS()
class WHENGINE_API RApplication : public RObject
{
    ROBJECT
public:
    //there is only one application, so use static function to get it
    static RApplication* GetApplication();

    RApplication();
    ~RApplication();

    //@ExecuteblePath file path, for determines important files' path
    RFUNCTION()
    void Initialize(const SApplicationArgument& Arguments);

    //keep running untill exit application,generally invoked in main()
    //if you just use engine as a embedded library and do not want change the main() function,
    //manually invoke RunOnce() is better 
    RFUNCTION()
    int Run();

    //run only one frame, you need invoke this function every frame
    //if return -1, means require stop application message loop
    RFUNCTION()
    int RunOnce();

    RFUNCTION()
    void Exit();

    void RunMainThreadTasks();

    SApplicationArgument& GetInitializeArguments()
    {
        return InitializeArguments;
    }
protected:
    SApplicationArgument InitializeArguments;

    TOptional<std::chrono::time_point<std::chrono::steady_clock>> 
        LastFrameTime;
    
    bool bRunning=true;

    void DispatchEvent();

public:
    CGarbageCollector* GC=nullptr;
    uint32_t GCInterval= 2000; // ms
    uint32_t GCTimer=0; // ms

public:
    //设置逻辑线程帧率限制，0表示不限制
    //默认不限制，但是在不限制帧率的情况下，CPU会被100%占用
    void SetLogicFrameLimit(uint32_t InFrameLimit)
    {
        FrameLimit=InFrameLimit;
    }

    //获取帧率
    float GetLogicFPS() const
    {
        return FramePerSecond;
    }
    
    void EnablePrintFPS()
    {
        bPrintFPS=true;
    }

    void DisablePrintFPS()
    {
        bPrintFPS=false;
    }
    
    bool IsPrintFPS()
    {
        return bPrintFPS;
    }

    void EnableGC()
    {
        bEnableGC=true;
    }

    void DisableGC()
    {
        bEnableGC=false;
    }

    bool IsEnableGC()
    {
        return bEnableGC;
    }



protected:
    bool bPrintFPS=false;
    bool bEnableGC=true;

    //逻辑线程帧率限制，0表示不限制,在不限制帧率的情况下，CPU会被100%占用
    //在帧率极高的情况下(比如900帧)在某些设备上会出现窗口闪烁的问题，目前不知道原因
    uint32_t FrameLimit=0;
    //逻辑线程帧率
    float FramePerSecond=0.0f;

public:

    template<typename SubsystemType>
    void MakeSureInitialized()
    {
        SubsystemManager->MakeSureInitialized<SubsystemType>();
    }

    template<typename SubsystemType>
    SubsystemType* GetSubsystem()
    {
        return SubsystemManager->GetSubsystem<SubsystemType>();
    }

    RFUNCTION()
    TSharedObjectPtr<RApplicationSubsystem> GetSubsystemByClass(TSubclassOf<RApplicationSubsystem> InClass)
    {
        return SubsystemManager->GetSubsystemByClass(InClass.Get());
    }    

protected:
    void InitializeSubsystems();
    void DeinitializeSubsystems();

    RFIELD()
    TSharedObjectPtr<RSubsystemManager> SubsystemManager;


public:

    /// @brief  Register a shortcut key, when key pressed, invoke the callback
    /// @param InKey 
    /// @param InCallBack  invoke when key pressed,return true means consumed
    void RegisterShorcut(
        const SShortcutKey& InKey, 
        const TScriptDelegate<bool()>& InCallBack)
    {
        Shortcuts.Add(InKey, InCallBack);
    }

    void UnregisterShorcut(const SShortcutKey& InKey)
    {
        Shortcuts.Remove(InKey);
    }

    bool IsShortcutRegistered(const SShortcutKey& InKey)
    {
        return Shortcuts.Contains(InKey);
    }

protected:
    TMap<SShortcutKey, TScriptDelegate<bool()>> Shortcuts;


//帧跳动
public:
    //注册帧跳对象
    void RegisterTickable(ITickable* InTickable);
    //注销帧跳对象
    void UnregisterTickable(ITickable* InTickable);
protected:
    CTickableManager TickableManager;

};
